﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.IO;

using STSdb.Data;

using STSdb.MTL.Utils.Serialization;
using STSdb.MTL.Utils.Bytes;

namespace STSdb.MTL.Backup
{
    public class Backup
    {
        StorageEngine _engine = null;
        string _pathOfBackup = String.Empty;

        public Backup(StorageEngine engine, string pathOfBackup)
        {
            this._pathOfBackup = pathOfBackup;
            this._engine = engine;
        }

        public class Backup_TableHeader
        {
            public string TableName { get; set; }
            public string KeyType { get; set; }
            public string RecordType { get; set; }
            public string KeyMap { get; set; }
        }

        public class Backup_Record
        {
            public string Key { get; set; }
            public string Value { get; set; }
        }

        public void StartBackup()
        {
            Backup_TableHeader th = null;
            Backup_Record tr = null; 
            string ser=String.Empty;
            byte[] bt=null;
            byte[] bt1 = null;
            string tableName = String.Empty;

            short operationType=0;

            try
            {
                File.Delete(_pathOfBackup);

                using (FileStream fs = new FileStream(_pathOfBackup, FileMode.Append, FileAccess.Write, FileShare.Read))
                {
                    try
                    {
                        foreach (var tn in _engine.Scheme.Forward())
                        {
                            tableName = tn.Key.ToString();

                            //Later can be extra logic
                            if (tableName.StartsWith("@sys_", StringComparison.OrdinalIgnoreCase))
                                continue;

                            th = new Backup_TableHeader();

                            var tbl = _engine.Scheme.OpenXTable(new Locator(tableName));
                            th.TableName = tableName;
                            th.KeyType = tbl.KeyType.ToString();
                            th.RecordType = tbl.RecordType.ToString();
                            th.KeyMap = tbl.KeyMap.ToString();

                            ser = th.SerializeXml();

                            bt = System.Text.Encoding.UTF8.GetBytes(ser);

                            //here must be 4 bytes lenght (len of rec + 2 operation type) + 2 byte operation type (short) + record itself
                            //this element has number 10

                            bt1 = (bt.Length + 2).To_4_bytes_array_BigEndian();
                            fs.Write(bt1, 0, bt1.Length);

                            operationType = 10;

                            bt1 = operationType.To_2_bytes_array_BigEndian();
                            fs.Write(bt1, 0, bt1.Length);

                            fs.Write(bt, 0, bt.Length);

                            foreach (var rec in tbl.Forward())
                            {
                                tr = new Backup_Record();
                                tr.Key = rec.Key.SerializeXml();
                                tr.Value = rec.Record.SerializeXml();

                                ser = tr.SerializeXml();
                                bt = System.Text.Encoding.UTF8.GetBytes(ser);

                                //here must be 4 bytes lenght (len of rec + 2 operation type) + 2 byte operation type (short) + record itself
                                //this element has number 11

                                bt1 = (bt.Length + 2).To_4_bytes_array_BigEndian();
                                fs.Write(bt1, 0, bt1.Length);

                                operationType = 11;

                                bt1 = operationType.To_2_bytes_array_BigEndian();
                                fs.Write(bt1, 0, bt1.Length);


                                fs.Write(bt, 0, bt.Length);
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        throw ex;
                    }
                    finally
                    {
                        fs.Close();
                    }

                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
            



            Console.WriteLine("Backup is done");
        }


        /// <summary>
        /// No support of XKeyMaps.
        /// No need in transaction table, or compacting table restore, they will be restored when backing up instance is started.
        /// </summary>
        public void RestoreBackup()
        {
            Backup_TableHeader th = null;
            Backup_Record tr = null; 
            string ser=String.Empty;
            byte[] bt=null;
            byte[] bt1 = null;
            byte[] bt2 = null;
            string tableName = String.Empty;

            //short operationType=0;

            XTable tbl = null;

            try
            {               

                using (FileStream fs = new FileStream(_pathOfBackup, FileMode.Open, FileAccess.Read))
                {
                    while (true)
                    {
                        bt=new byte[4];
                        //Total length
                        if (fs.Read(bt, 0, bt.Length) < 1)
                            break;

                        bt1 = new byte[2];
                        //operation type
                        if (fs.Read(bt1, 0, bt1.Length) < 1)
                            break;

                        bt2 = new byte[bt.To_Int32_BigEndian()-2];
                        //value self
                        if (fs.Read(bt2, 0, bt2.Length) < 1)
                            break;

                        switch (bt1.To_Int16_BigEndian())
                        {
                            case 10:
                                if (tbl != null)
                                    tbl.Commit();

                                //new table
                                th = System.Text.Encoding.UTF8.GetString(bt2).DeserializeXml<Backup_TableHeader>();
                                tbl = _engine.Scheme.CreateXTable(new Locator(th.TableName),Type.GetType(th.KeyType),Type.GetType(th.RecordType));
                                _engine.Scheme.Commit();

                                //NO SUPPORT OF KEY MAPS, FOR NOW
                                //  tbl.KeyMap = KeyMapBuilder.Build(Type.GetType(th.KeyMap),null,

                                break;
                            case 11:
                                //new table record
                                tr = System.Text.Encoding.UTF8.GetString(bt2).DeserializeXml<Backup_Record>();

                                object a = tr.Key.DeserializeXml(Type.GetType(th.KeyType));
                                if (th.RecordType == "System.Object")
                                    th.RecordType = "System.String";
                                object b = tr.Value.DeserializeXml(Type.GetType(th.RecordType));
                                tbl[a] = b;

                                break;
                            default:

                                tbl = null;
                                break;
                        }

                    }

                    if (tbl != null)
                        tbl.Commit();
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }

            Console.WriteLine("Restore is done");
        }


    }
}
